#ifndef LOGIC_H
#define LOGIC_H

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <ctype.h>
#include <algorithm>

// FOR LOGGING ERRORS TO FILE
#include <fstream>

#include "Parser.h"
#include "Data.h"
#include "Task.h"
#include "Writer.h"

#include "boost/date_time/posix_time/posix_time.hpp"

using namespace std;
using namespace boost::posix_time;

class Logic {

private:
	// LOGIC MESSAGE
	static const string INVALID_COMMAND_MESSAGE;

	// TASK MESSAGE
	static const string ADD_SUCCESS_MESSAGE;
	static const string DELETE_SUCCESS_MESSAGE;
	static const string CLEAR_SUCCESS_MESSAGE;
	static const string LIST_SUCCESS_MESSAGE;
	static const string SORT_SUCCESS_MESSAGE;
	static const string EDIT_SUCCESS_MESSAGE;
	static const string SEARCH_SUCCESS_MESSAGE;
	static const string FLAG_SUCCESS_MESSAGE;
	static const string UNFLAG_SUCCESS_MESSAGE;

	// ERROR MESSAGES
	static const string ADD_ERROR_MESSAGE;
	static const string DELETE_ERROR_MESSAGE;
	static const string CLEAR_ERROR_MESSAGE;
	static const string LIST_ERROR_MESSAGE;
	static const string SORT_ERROR_MESSAGE;
	static const string EDIT_ERROR_MESSAGE;
	static const string FLAG_ERROR_MESSAGE;
	static const string UNFLAG_ERROR_MESSAGE;
	static const string NOT_FOUND_ERROR_MESSAGE;

	// ERROR LOG FILE NAME
	static const string ERROR_LOG_FILE_NAME;

	// 5 PRIORITY LEVELS
	static const int PRIORITY_LEVEL_1;
	static const int PRIORITY_LEVEL_2;
	static const int PRIORITY_LEVEL_3;
	static const int PRIORITY_LEVEL_4;
	static const int PRIORITY_LEVEL_5;

	// UNDO STATES
	static const int INIT_UNDO_STATE;
	static const int MAX_UNDO_STATES;

	// KEYWORD
	static const string DELETE_TYPE_DONE;

	// ADD ERROR
	enum AddStatus {
		DATE_CLASH,
		NAME_REPEAT,
		SUCCESS
	};

	// DELETE TYPE
	enum DeleteType {
		INDEX,
		NAME,
		SEARCH_TERM,
		DONE
	};

	// EXECUTION TYPES
	enum ExecutionType {
		ADD_FLOATING, ADD_DEADLINE, ADD_TIMED,
		DELETE_INDEX, DELETE_NAME, DELETE_SEARCH_TERM, DELETE_DONE,
		CLEAR,
		LIST_ALL, LIST_MONTH, LIST_DAY, LIST_WEEK, LIST_DATE, LIST_SEARCH_TERM
		// edit
		// flag
		// done
		// sort
		// undo
	};

	Parser _parser;
	Data _data;
	Task _task;
	Writer _writer;

	// support up to 10 undo
	// 11 writer states (including the main _writer)
	// cycles through the 10 states
	int _undoState;
	Writer _undoState0;
	Writer _undoState1;
	Writer _undoState2;
	Writer _undoState3;
	Writer _undoState4;
	Writer _undoState5;
	Writer _undoState6;
	Writer _undoState7;
	Writer _undoState8;
	Writer _undoState9;

	// add
	bool isExistName(Parser::PARSED_COMMAND parsedCommand);
	bool isDateClash(Parser::PARSED_COMMAND parsedCommand);
	string addFloatingTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string addDeadlineTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string addTimedTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// delete
	bool isDeleteByIndex(string term); // returns true for int and false for string
	bool isDeleteByDone(string term); // returns true if matches done keyword and false otherwise
	bool isDeleteByName(string term); // returns true if found in vector and false otherwise
	bool isDeleteBySearchTerm(string term); // returns true if found in vector and false otherwise
	DeleteType determineDeleteType(Parser::PARSED_COMMAND parsedCommand);
	string deleteTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteDoneTask(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string deleteTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// clear
	string clearTask();

	// list
	string listAllTasks(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskBySearchTerm(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDay(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDate(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskNextWeek(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByMonth(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string listTaskByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// edit
	string editTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string editTaskByDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// sort
	// string sortTasks(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksAlphabetically(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByFlag(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByDateTime(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string sortTasksByDone(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// flag
	string flagTaskByName(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);
	string flagTaskByIndex(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// help
	string help(Parser::PARSED_COMMAND parsedCommand);

	// undo
	void updateUndoState();
	int getUndoState();
	string undo(Parser::PARSED_COMMAND parsedCommand, vector<Task>& tasksVector);

	// log error messages
	void logErrors(string errorMessage, int undoState);

	// setup
	void setupMainWriter();
	void setupWriters();
	void setInitUndoState(int initUndoState);
	void populateData();

	// helper functions
	void emptyTasksVector(vector<Task>& tasksVector);
	string getStatusMessage(Data::DataFeedback dataFeedback);
	// for refactoring
	// void copyVectorContents(vector<Task>& tasks, vector<Task>& tasksVector);

	// for sorting
	bool sortAlpha(Task t1, Task t2);
	struct sortA {
		bool operator() (Task t1, Task t2) { return t1.getTaskName() < t2.getTaskName(); };
	} sortObjAlpha;
	bool sortDateTime(Task t1, Task t2);
	struct sortT {
		bool operator() (Task t1, Task t2) { return t1.getStartTime() < t2.getStartTime(); };
	} sortObjDateTime;

public:
	// execution for UI
	string executeUserCommand(string userCommand, vector<Task>& tasksVector);
	void setup();

	Logic();
	~Logic();
};

#endif ILOGIC_H